﻿using LPBToolsLib.NetSocket;
using LPBToolsLib;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace LPBToolsNF.NetSocket
{
    public class AsyncSerClient : ContainerBase
    {
        /// <summary>
        /// 客户端主动断开
        /// </summary>
        public event SocketEventCallBack OnDisconnect = null;
        /// <summary>
        /// 服务器清理无效连接断开
        /// </summary>
        public event SocketEventCallBack AfterDisconnect = null;
        /// <summary>
        /// 客户端收到数据的处理事件
        /// </summary>
        public event SocketRecDataCallBack OnReceive = null;
        public Recorder_Error ErrRecorder { get; set; } = null;

        [DisplayName("丢失连接的超时，单位：ms")]
        public int LostConnectedTimeOut { get; } = 0;
        [DisplayName("连接最后活动时间")]
        public DateTime LastActive { get; private set; } = DateTime.Now;
        [DisplayName("连接客户端地址")]
        public string EndPoint { get { return ((ClientSocket != null) && (ClientSocket.RemoteEndPoint != null)) ? ClientSocket.RemoteEndPoint.ToString() : ""; } }

        /// <summary>
        /// 客户连接Socket
        /// </summary>
        public Socket ClientSocket { get; }
        /// <summary>
        /// 接受数据缓冲区
        /// </summary>
        protected byte[] RecBuf { get; } = new byte[1024];
        [DisplayName("接受数据缓冲区大小")]
        public int RecBufSize { get { return RecBuf.Length; } }

        [DisplayName("客户端连接是否有效")]
        public bool Active
        {
            get
            {
                if (LostConnectedTimeOut > 0)
                {
                    TimeSpan tDef = DateTime.Now - LastActive;
                    return Connected && (tDef.TotalMilliseconds < LostConnectedTimeOut);
                }
                return Connected;
            }
        }
        [DisplayName("客户端是否在线")]
        public bool Connected { get { return (ClientSocket != null) ? ClientSocket.Connected : false; } }

        public AsyncSerClient(Socket Client, int LostConnectedTO_ms = 0)
        {
            LostConnectedTimeOut = LostConnectedTO_ms;
            ClientSocket = Client;
            if (ClientSocket != null)
            {
                ClientSocket.BeginReceive(RecBuf, 0, RecBuf.Length, SocketFlags.None,
                    new AsyncCallback(ReceiveCallback), ClientSocket);
            }
        }

        /// <summary>
        /// 接受数据回调函数
        /// </summary>
        /// <param name="asyncResult"></param>
        private void ReceiveCallback(IAsyncResult asyncResult)
        {
            LastActive = DateTime.Now;
            if (Connected)
            {
                byte[] tTRBuf = null;
                try
                {
                    int length = ClientSocket.EndReceive(asyncResult);
                    if (length > 0)
                    {
                        if (length < RecBuf.Length)
                        {
                            List<byte> tRecDatas = new List<byte>();
                            for (int i = 0; i < length; i++)
                                tRecDatas.Add(RecBuf[i]);
                            tTRBuf = tRecDatas.ToArray();
                        }
                        if (tTRBuf != null)
                        {
                            OnReceive?.Invoke(this, ClientSocket, ref tTRBuf);
                            if ((tTRBuf != null) && (tTRBuf.Length > 0))
                                Send(tTRBuf);
                        }
                        if (ClientSocket.Connected)
                            ClientSocket.BeginReceive(RecBuf, 0, RecBuf.Length, SocketFlags.None,
                            new AsyncCallback(ReceiveCallback), ClientSocket);
                    }
                    else
                    {
                        ClientSocket.Close();
                        OnDisconnect?.Invoke(this, ClientSocket);
                    }
                }
                catch(Exception ex)
                {
                    ErrRecorder?.setRecord(ex);
                    ClientSocket?.Close();
                }
                finally
                {
                    //ClientSocket.Close();
                }
            }
        }

        public void CloseAll()
        {
            if (ClientSocket != null)
            {
                if (ClientSocket.Connected)
                    AfterDisconnect?.Invoke(this, ClientSocket);
                ClientSocket.Close();
            }
        }

        public int Send(byte[] datas)
        {
            if (Connected)
                return ClientSocket.Send(datas);
            return 0;
        }
    }

    /// <summary>
    /// 异步方式的TCP服务器
    /// </summary>
    public class TCPAsyncServer : ContainerBase
    {
        /// <summary>
        /// 客户端成功连接到服务器事件
        /// </summary>
        public event SocketEventCallBack OnConnect = null;
        /// <summary>
        /// 客户端主动断开连接事件
        /// </summary>
        public event SocketEventCallBack OnDisconnect = null;
        /// <summary>
        /// 客户端超时被断开事件
        /// </summary>
        public event SocketEventCallBack AfterDisconnect = null;
        /// <summary>
        /// 收到客户端发送的数据事件
        /// </summary>
        public event SocketRecDataCallBack OnReceive = null;
        /// <summary>
        /// 客户端无操作时的超时设置，单位：ms
        /// </summary>
        [DisplayName("客户端无操作时的超时设置，单位：ms")]
        public int LostConnectedTimeOut { get; set; } = 10000;

        public Recorder_Error ErrRecorder { get; set; } = null;

        private string sMutexKey = "TCPAsyncServer20230728" + Guid.NewGuid().ToString();

        private Mutex tMutex { get; }
        /// <summary>
        /// 客户端管理对象列表
        /// </summary>
        protected Dictionary<EndPoint, AsyncSerClient> tClients { get; } = new Dictionary<EndPoint, AsyncSerClient>();

        protected Socket SerSocket { get; private set; }
        /// <summary>
        /// 异步方式的TCP服务器
        /// </summary>
        /// <param name="DestServerEP">待绑定的服务器地址</param>
        public TCPAsyncServer(IPEndPoint DestServerEP = null)
        {
            tMutex = new Mutex(false, sMutexKey);
            ServerEP = DestServerEP;
        }
        [DisplayName("当前绑定的服务器地址")]
        public string ServerEPStr { get { return (ServerEP != null) ? ServerEP.ToString() : ""; } }
        /// <summary>
        /// 当前绑定的服务器地址
        /// </summary>
        public IPEndPoint ServerEP { get; protected set; }
        /// <summary>
        /// 服务器是否正常工作
        /// </summary>
        [DisplayName("服务器是否正常工作")]
        public bool IsListen { get { return (SerSocket != null) && (ServerEP != null) && SerSocket.IsBound && thdRun; } }
        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="backlog">服务器最多能够同时提供的在线连接数，0：不限制</param>
        public void StarListen(int backlog = 0)
        {
            if (ServerEP != null)
            {
                SerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //绑定端口和IP
                SerSocket.Bind(ServerEP);
                if (backlog > 0)
                    SerSocket.Listen(backlog);
                else
                    SerSocket.Listen(0);
                //连接客户端
                SerSocket.BeginAccept(new AsyncCallback(AcceptCallBack), SerSocket);
                thdRun = true;
                tThdDaemon = new Thread(thdMainRun);
                tThdDaemon.Start();
            }
        }

        /// <summary>
        /// 启动服务器
        /// </summary>
        /// <param name="DestServerEP">等待绑定的服务器地址</param>
        /// <param name="backlog">服务器最多能够同时提供的在线连接数，0：不限制</param>
        public void StarListen(IPEndPoint DestServerEP, int backlog = 0)
        {
            ServerEP = DestServerEP;
            StarListen(backlog);
        }

        /// <summary>
        /// 异步处理程序：
        /// 有客户端连接时的回调函数
        /// </summary>
        /// <param name="asyncResult"></param>
        private void AcceptCallBack(IAsyncResult asyncResult)
        {
            if (thdRun)
            {
                //获取客户端套接字
                Socket client = SerSocket.EndAccept(asyncResult);
                AsyncSerClient tClientThd = null;
                if (client != null)
                {
                    tMutex.WaitOne();
                    try
                    {
                        EndPoint tClientEP = client.RemoteEndPoint;

                        //if (tClients.ContainsKey(tClientEP))
                        //{
                        //    tClients[tClientEP].CloseAll();
                        //    tClients.Remove(tClientEP);
                        //}

                        if (!tClients.ContainsKey(tClientEP))
                        {
                            tClientThd = new AsyncSerClient(client, LostConnectedTimeOut);
                            tClientThd.OnDisconnect += OnDisconnect;
                            tClientThd.AfterDisconnect += TClientThd_AfterDisconnect;
                            tClientThd.AfterDisconnect += AfterDisconnect;
                            tClientThd.OnReceive += TClientThd_OnReceive; ;
                            tClientThd.ErrRecorder = ErrRecorder;
                            // 由于客户端的连接非常短，可能提交数据后立刻断开，因此必须预防访问已经断开的连接
                            if (client.Connected)
                                tClients.Add(tClientEP, tClientThd);
                            else
                                tClientThd = null;
                        }
                    }
                    finally
                    {
                        tMutex.ReleaseMutex();
                    }
                }
                try
                {
                    if ((client != null) && client.Connected && (tClientThd != null))
                        OnConnect?.Invoke(this, client);
                }
                finally
                {
                    SerSocket.BeginAccept(new AsyncCallback(AcceptCallBack), SerSocket);
                }
            }
        }

        private void TClientThd_AfterDisconnect(object sender, Socket ClientSocket)
        {
            EndPoint tClientEP = ClientSocket.RemoteEndPoint;
            if (tClients.ContainsKey(tClientEP))
            {
                tMutex.WaitOne();
                try
                {
                    //tClients[tClientEP].CloseAll();
                    tClients.Remove(tClientEP);
                }
                finally
                {
                    tMutex.ReleaseMutex();
                }
            }
        }

        /// <summary>
        /// 内部函数：当收到客户端的数据后的处理函数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="ClientSocket"></param>
        /// <param name="RecData"></param>
        private void TClientThd_OnReceive(object sender, Socket ClientSocket, ref byte[] RecData)
        {
            OnReceive?.Invoke(this, ClientSocket, ref RecData);
        }

        /// <summary>
        /// 异步向客户端发送消息
        /// </summary>
        /// <param name="client"></param>
        /// <param name="datas"></param>
        public void SendAsync(Socket client, byte[] datas)
        {
            if ((client != null) && client.Connected
                && (datas != null) && (datas.Length > 0))
            {
                try
                {
                    //开始发送消息
                    client.BeginSend(datas, 0, datas.Length, SocketFlags.None, asyncResult =>
                    {
                        //完成消息发送
                        int length = client.EndSend(asyncResult);
                    }, null);
                }
                catch (Exception e)
                {
                    Console.WriteLine(e.Message);
                }
            }
        }

        public void SendAsync(Socket client, string datas)
        {
            if (!string.IsNullOrEmpty(datas))
                SendAsync(client, Encoding.UTF8.GetBytes(datas));
        }

        /// <summary>
        /// 清场函数，关闭所有客户端的连接
        /// </summary>
        public void CloseAll()
        {
            thdRun = false;
            Thread.Sleep(1);
            tMutex.WaitOne();
            try
            {
                EndPoint[] tClientEPList = tClients.Keys.ToArray();
                if (tClientEPList.Length > 0)
                {
                    foreach (EndPoint tClientEP in tClientEPList)
                    {
                        tClients[tClientEP].CloseAll();
                        tClients.Remove(tClientEP);
                    }
                }
            }
            finally
            {
                tMutex.ReleaseMutex();
            }
            SerSocket?.Close();
            SerSocket = null;
        }

        #region 客户端维护
        private Thread tThdDaemon { get; set; }
        private bool thdRun { get; set; } = false;

        /// <summary>
        /// 客户端无操作超时管理
        /// </summary>
        private void thdMainRun()
        {
            while (thdRun)
            {
                if (tClients.Count > 0)
                {
                    List<EndPoint> tDelList = new List<EndPoint>();
                    tMutex.WaitOne();
                    try
                    {
                        foreach (KeyValuePair<EndPoint, AsyncSerClient> Client in tClients)
                            if (!Client.Value.Active)
                                tDelList.Add(Client.Key);
                        if (tDelList.Count > 0)
                            foreach (EndPoint tClientEP in tDelList)
                            {
                                tClients[tClientEP].CloseAll();
                                tClients.Remove(tClientEP);
                            }
                    }
                    finally
                    {
                        tMutex.ReleaseMutex();
                    }
                }
                Thread.Sleep(1);
            }
        }

        /// <summary>
        /// 返回当前客户端连接数量
        /// </summary>
        [DisplayName("当前客户端连接数量")]
        public int ClientsCount { get { return tClients.Count; } }

        /// <summary>
        /// 返回所有在线客户端的信息
        /// </summary>
        /// <returns></returns>
        [DisplayName("所有在线客户端的信息")]
        public Dictionary<string, object>[] getClientsList()
        {
            List<Dictionary<string, object>> tResult = new List<Dictionary<string, object>>();
            if (tClients.Count > 0)
            {
                AsyncSerClient[] tClientInfos = tClients.Values.ToArray();
                Dictionary<string, object> tClientInfo = null;
                foreach (AsyncSerClient tClient in tClientInfos)
                    if (tClient.toDetail(out tClientInfo))
                        tResult.Add(tClientInfo);
            }
            return tResult.ToArray();
        }
        #endregion
    }
}
